<?php

namespace App\Admin\Form;

use App\Admin\Admin;
use App\Admin\Form;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Support\Arr;

/**
 * Class Field
 *
 * @method Field default($value) 设置组件的显示的默认值
 */
class Field implements Renderable
{
    /**
     * 标签文字最小宽度
     */
    const MIN_LABEL_WIDTH = 150;

    /**
     * 输入区最小宽度
     */
    const MIN_FIELD_WIDTH = 190;

    /**
     * 字段名(数据库中的
     *
     * @var string
     */
    protected $column = '';

    /**
     * 组件中标签文字
     *
     * @var string
     */
    protected $label = '';

    /**
     * 组件后边的提示性文字
     *
     * @var string
     */
    protected $help = '';

    /**
     * 该组件的html元素id
     *
     * @var mixed
     */
    protected $id;

    /**
     * 表单实例
     *
     * @var Form
     */
    protected $form;

    /**
     * 组件的值
     *
     * @var mixed
     */
    protected $value;

    /**
     * 组件特定的js
     *
     * @var string
     */
    protected $script = '';

    /**
     * 传入该组件的blade中用的所有值
     *
     * @var array
     */
    protected $variables = [];

    /**
     * 组件要显示的默认值
     *
     * @var mixed
     */
    protected $default;

    /**
     * 组件的一些设置项,比如下拉的选择项,前端js的配置项等
     *
     * @var array
     */
    protected $options = [];

    /**
     * 字段原始的值,即更新之前的值
     *
     * @var mixed
     */
    protected $original;

    /**
     * 组件html元素的属性
     *
     * @var array
     */
    protected $attributes = [];

    /**
     * 组件html元素的placeholder值
     *
     * @var string|array
     */
    protected $placeholder;

    /**
     * 组件使用的blade模板
     *
     * @var string
     */
    protected $view = '';

    /**
     * 组件标签文字和输入区的宽度
     *
     * @var array
     */
    protected $width = [
        'label' => 150,
        'field' => 380,
    ];

    /**
     * 表单元素的name值
     *
     * @var string
     */
    protected $elementName = [];

    /**
     * 表单元素的class值
     *
     * @var array
     */
    protected $elementClass = [];

    /**
     * 组件所需的特定css文件
     *
     * @var array
     */
    protected static $css = [];

    /**
     * 组件所需的特定js文件
     *
     * @var array
     */
    protected static $js = [];

    /**
     * Field constructor.
     *
     * @param string|array $column 字段名
     * @param array        $arguments
     */
    public function __construct($column, $arguments = [])
    {
        $this->column = $column;
        $this->label = $this->formatLabel($arguments);
        $this->id = $this->formatId($column);
    }

    /**
     * 生成组件html中标签的内容
     *
     * @param array $arguments 标签的内容
     *
     * @return string
     */
    protected function formatLabel($arguments = [])
    {
        $column = is_array($this->column) ? current($this->column) : $this->column;

        $label = isset($arguments[0]) ? $arguments[0] : ucfirst($column);

        return str_replace(['.', '_'], ' ', $label);
    }

    /**
     * 生成组件html元素的id
     *
     * @param $column
     *
     * @return mixed
     */
    protected function formatId($column)
    {
        return str_replace(['.', '_'], ' ', $column);
    }

    /**
     * 生成表单输入框的name属性
     *
     * @param $column
     *
     * @return mixed|string
     */
    protected function formatName($column)
    {
        // 把 xxx.yy.zz的属性转成xxx[yy][zz]
        if (is_string($column)) {
            $name = explode('.', $column);

            if (count($name) == 1) {
                return $name[0];
            }

            $html = array_shift($name);
            foreach ($name as $piece) {
                $html .= "[{$piece}]";
            }

            return $html;
        }

        if (is_array($this->column)) {
            $names = [];
            foreach ($this->column as $key => $name) {
                $names[$key] = $this->formatName($name);
            }

            return $names;
        }

        return '';
    }

    /**
     * 当表单为编辑或者查看模式时,设置组件的值
     *
     * @param $data
     */
    public function fill($data)
    {
        if (is_array($this->column)) {
            foreach ($this->column as $key => $column) {
                $this->value[$key] = array_get($data, $column);
            }

            return;
        }

        $this->value = array_get($data, $this->column);
    }

    /**
     * 以正确的html元素格式(即:属性="值"),返回元素的属性和值
     *
     * @return string
     */
    protected function formatAttributes()
    {
        $html = [];

        foreach ($this->attributes as $name => $value) {
            $html[] = "$name=\"{$value}\"";
        }

        return implode(' ', $html);
    }

    /**
     * 设置或者获取字段的值
     *
     * @param null $value
     *
     * @return $this|mixed
     */
    public function value($value = null)
    {
        if (is_null($value)) {
            if (is_null($this->default)) {
                // $this->default = \Request::get($this->column);
            }

            return is_null($this->value) ? $this->getDefault() : $this->value;
        }

        $this->value = $value;

        return $this;
    }

    /**
     * 设置表单实例
     *
     * @param Form $form
     *
     * @return $this
     */
    public function setForm(Form $form)
    {
        $this->form = $form;

        return $this;
    }

    /**
     * 设置组件的可选值
     *
     * @param string|array $options
     * @param null|mixed
     *
     * @return $this
     */
    public function options($options, $value = null)
    {
        if ($options instanceof Arrayable) {
            $options = $options->toArray();
        } elseif (is_string($options) || is_integer($options)) {
            $options = [$options => $value];
        } elseif (!is_array($options)) {
            throw new \InvalidArgumentException('参数错误');
        }

        $this->handleOptions($options);

        return $this;
    }

    public function getOption($key = null)
    {
        if (is_null($key)) {
            return $this->options;
        }

        return $this->options[$key] ?? null;
    }

    /**
     * 处理
     * 由于某些组件的处理方式不同,所以这里抽出一个方法,供子类覆盖,而不用重写整个options方法
     *
     * @param $options
     */
    protected function handleOptions($options)
    {
        $this->options = array_merge($this->options, $options);
    }

    /**
     * 生成该组件的html代码
     *
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
     */
    public function render()
    {
        Admin::script($this->script);

        return view($this->getView(), $this->variables());
    }

    /**
     * 获取该组件使用的blade模板
     *
     * @return string
     */
    public function getView()
    {
        if (!empty($this->view)) {
            return $this->view;
        }

        $class = explode('\\', get_called_class());

        return 'admin.form.'.strtolower(end($class));
    }

    public function variables()
    {
        $this->variables = array_merge($this->variables, [
            'id'          => $this->id,
            'name'        => $this->elementName ?: $this->formatName($this->column),
            'help'        => $this->help,
            'class'       => $this->getElementClassString(),
            'value'       => $this->value(),
            'label'       => $this->label,
            'width'       => $this->width,
            'column'      => $this->column,
            // 'errorKey'      => $this->getErrorKey(),
            'attributes'  => $this->formatAttributes(),
            'placeholder' => $this->getPlaceholder(),
        ]);

        $this->variables = array_merge($this->variables, $this->additionalVariables());

        return $this->variables;
    }

    public function help(string $help)
    {
        $this->help = $help;

        return $this;
    }

    /**
     * 其他表单组件自己的需要传到模板中用的数据
     *
     * @return array
     */
    protected function additionalVariables()
    {
        return [];
    }

    public function __call($method, $arguments)
    {
        // default 不能用作函数名
        if ($method === 'default') {
            return $this->setDefault(array_get($arguments, 0));
        }
    }

    /**
     * 设置组件默认值
     *
     * @param $default
     *
     * @return $this
     */
    public function setDefault($default)
    {
        $this->default = $default;

        return $this;
    }

    /**
     * 返回组件的字段名
     *
     * @return array|string
     */
    public function column()
    {
        return $this->column;
    }

    /**
     * 设置组件对应的字段更新前的原始值
     *
     * @param $data
     */
    public function setOriginal($data)
    {
        if (is_array($this->column)) {
            foreach ($this->column as $key => $column) {
                $this->original[$key] = array_get($data, $column);
            }

            return;
        }

        $this->original = array_get($data, $this->column);
    }

    /**
     * 获取组件对应的字段更新前的原始值
     *
     * @return mixed
     */
    public function original()
    {
        return $this->original;
    }

    public function setWidth($field = 380, $label = 150)
    {
        list($field, $label) = static::checkWidth($field, $label);

        $this->width = [
            'label' => $label,
            'field' => $field,
        ];

        return $this;
    }

    /**
     * 检查并返回正确的宽度值
     *
     * @param $field
     * @param $label
     *
     * @return array
     */
    public static function checkWidth($field, $label)
    {
        if ($field <= self::MIN_FIELD_WIDTH) {
            $field = self::MIN_FIELD_WIDTH;
        }

        if ($label <= self::MIN_LABEL_WIDTH) {
            $label = self::MIN_LABEL_WIDTH;
        }

        return [$field, $label];
    }

    /**
     * 返回组件的html字符串
     *
     * @return string
     * @throws \Throwable
     */
    public function __toString()
    {
        return $this->render()->render();
    }

    /**
     * 设置组件html元素的class值
     *
     * @param $class
     *
     * @return $this
     */
    public function addElementClass($class)
    {
        if (is_array($class) || is_string($class)) {
            $this->elementClass = array_merge($this->elementClass, (array) $class);

            $this->elementClass = array_unique($this->elementClass);
        }

        return $this;
    }

    /**
     * 移除组件html元素的class值
     *
     * @param $class
     *
     * @return $this
     */
    public function removeElementClass($class)
    {
        $delClass = [];

        if (is_array($class) || is_string($class)) {
            $delClass = (array) $class;
        }

        foreach ($delClass as $del) {
            if (($key = array_search($del, $this->elementClass))) {
                unset($this->elementClass[$key]);
            }
        }

        return $this;
    }

    /**
     * 设置组件html元素的属性和值
     *
     * @param      $attribute
     * @param null $value
     *
     * @return $this
     */
    public function attribute($attribute, $value = null)
    {
        if (is_array($attribute)) {
            $this->attributes = array_merge($this->attributes, $attribute);
        } else {
            $this->attributes[$attribute] = (string) $value;
        }

        return $this;
    }

    /**
     * 返回组件所有的js和css文件路径
     *
     * @return array
     */
    public static function getAssets()
    {
        return [
            'js'  => static::$js,
            'css' => static::$css,
        ];
    }

    /**
     * 返回组件的默认值
     *
     * @return mixed
     */
    public function getDefault()
    {
        if ($this->default instanceof \Closure) {
            return call_user_func($this->default, $this->form);
        }

        return $this->default;
    }

    /**
     * 获取该组件html元素所有class的数组
     *
     * @return array
     */
    protected function getElementClass()
    {
        // if (!$this->elementClass) {
        //     $name = $this->elementName ?: $this->formatName($this->column);
        //
        //     $this->elementClass = (array)str_replace(['[', ']'], '_', $name);
        // }

        return $this->elementClass;
    }

    /**
     * 获取元素的class选择器,就是所有class前面加个"."
     *
     * @return array|string
     */
    protected function getElementClassSelector()
    {
        $elementClass = $this->getElementClass();

        if (Arr::isAssoc($elementClass)) {
            $classes = [];

            foreach ($elementClass as $index => $class) {
                $classes[$index] = '.'.$class;
            }

            return $classes;
        }

        return '.'.implode('.', $elementClass);
    }

    /**
     * 获取组件html元素所有class的字符串值,用空格隔开
     *
     * @return array|string
     */
    protected function getElementClassString()
    {
        $elementClass = $this->getElementClass();

        if (Arr::isAssoc($elementClass)) {
            return $elementClass;
        }

        return implode(' ', $elementClass);
    }

    /**
     * 设置组件html元素的placeholder值
     *
     * @param string $placeholder
     *
     * @return $this
     */
    public function placeholder($placeholder = '')
    {
        $this->placeholder = $placeholder;

        return $this;
    }

    /**
     * 获取组件html元素的的placeholder值
     *
     * @return string
     */
    public function getPlaceholder()
    {
        return $this->placeholder;
    }

    /**
     * 获取组件html里面的js代码
     *
     * @return string
     */
    public function getScript()
    {
        return $this->script;
    }

    /**
     * 获取该组件的标签文字
     *
     * @return string
     */
    public function label()
    {
        return $this->label;
    }

    /**
     * 设置只读
     *
     * @return Field
     */
    public function readOnly()
    {
        return $this->attribute('disabled', true);
    }

    /**
     * 清理请求中的输入数据
     * TODO 完全只是对MultipleSelect组件的?
     *
     * @param $input
     * @param $column
     *
     * @return mixed
     */
    protected function sanitizeInput($input, $column)
    {
        return $input;
    }

    /**
     * 设置该组件html元素的class值
     *
     * @param $class
     *
     * @return $this
     */
    public function setElementClass($class)
    {
        $this->elementClass = (array) $class;

        return $this;
    }

    /**
     * 设置组件html元素的name值
     *
     * @param $name
     *
     * @return $this
     */
    public function setElementName($name)
    {
        $this->elementName = $name;

        return $this;
    }
}